#include "datamanipulator.h"
#include <QPainter>
DataManipulator::DataManipulator()
	:scene(0),root(0),current(0),enterHistory(),proj_opened(false)
{

}
void DataManipulator::SetDirty()
{
	current->SetDirty();
	for(int i=0;i!=enterHistory.size();++i)
		enterHistory[i]->SetDirty();
}
DataManipulator::~DataManipulator()
{
	if(scene)
		delete scene;
	if(root)
		delete root;
	current=0;
}
void DataManipulator::CloseProject()
{
	if(proj_opened)
	{
		if(scene)
		{
			delete scene;
			scene=0;
		}
		if(root)
		{
			delete root;
			root=0;
		}
		current=0;
		proj_opened=false;
		enterHistory.clear();
	}
}
void DataManipulator::CreateProject(QString name,int width,int height,QColor bkcolor,int fps)
{
	CloseProject();
	scene=new SceneNode();
	scene->name=name;
	scene->BackgroundColor=bkcolor;
	scene->FPS=fps;
	scene->Height=height;
	scene->Width=width;
	root=new MovieClipNode();
	current=root;
	proj_opened=true;

}
void DataManipulator::LoadProject(SceneNode* SceneData,MovieClipNode* SceneRoot)
{
	CloseProject();
	scene=SceneData;
	root=SceneRoot;
	current=root;
	proj_opened=true;
}
void DataManipulator::EnterIntoChild(int index)
{
	if(current->IsAtomic()||index<0||index> ((ContainerNode *)current)->ChildrenCount()  )
		throw "Failed to Enter!!!";
	else
	{
		enterHistory.push(current);
		current=((ContainerNode *)current)->ChildrenAt(index);
	}
}
void DataManipulator::EscapeToParent()
{
	if(enterHistory.size())
		current=enterHistory.pop();
	else
		throw "Already at root!!";
}
void DataManipulator::NewMovieClip(int index)
{
	if(current->IsAtomic())
		throw "Failed to Create!!!";
	if(index<0||index> ((ContainerNode *)current)->ChildrenCount()  )
		index=((ContainerNode *)current)->ChildrenCount();
	((ContainerNode *)current)->AddChildren(new MovieClipNode(),index);
	SetDirty();
}
void DataManipulator::BeginDrawingAt(int index)
{
	if(current->NodeType()==MovieClip)
	{
		if(index<0||index>=((ContainerNode *)current)->ChildrenCount())
			index=((ContainerNode *)current)->ChildrenCount();
		((ContainerNode *)current)->AddChildren(new SymbolNode(),index);
		enterHistory.push(current);
		current=((ContainerNode *)current)->ChildrenAt(index);

	}
	else
		throw "Failed to add symbol!!!";
	SetDirty();
}
void DataManipulator::ConvertToPath(int index)
{
	if(current->NodeType()==Symbol)
	{
		if(index<0||index>=((ContainerNode *)current)->ChildrenCount())
			throw "Invalid index!!!";
		if( ((ContainerNode *)current)->ChildrenAt(index)->NodeType()==Path || ((ContainerNode *)current)->ChildrenAt(index)->NodeType()==Picture)
			throw "Invalid source node!!!";;
		ShapeNode * temp=(ShapeNode *) ( ((ContainerNode *)current)->ChildrenAt(index) );
		PathNode * newNode=new PathNode(temp);

		((ContainerNode *)current)->ReplaceChildrenAt(index,newNode);
	}
	SetDirty();
}
void DataManipulator::ConvertToImage(int index)
{
	if(current->IsAtomic()||index<0||index> ((ContainerNode *)current)->ChildrenCount()  )
		throw "Invalid convert!!!";
	DisplayNode * temp= ((ContainerNode *)current)->ChildrenAt(index) ;
	QImage image((int)(temp->CacheOffsetX()+temp->GetWidth()),(int)(temp->CacheOffsetY()+temp->GetHeight()),QImage::Format_ARGB32);
	QPainter ctx(&image);
	ctx.translate(temp->CacheOffsetX(),temp->CacheOffsetY());
	ctx.drawImage(0,0,temp->GetImage());
	PictureNode* newNode=new PictureNode(image);
	newNode->CopyProperty(temp);
	if(current->NodeType()==MovieClip)
	{
		SymbolNode* temp1=new SymbolNode();
		temp1->AddChildren(newNode);
		((ContainerNode *)current)->ReplaceChildrenAt(index,temp1);
	}
	else
		((ContainerNode *)current)->ReplaceChildrenAt(index,newNode);
	SetDirty();
}
void DataManipulator::ConvertToMovieClip(int index)
{
	if(current->NodeType()==MovieClip
		&&index>=0
		&&index< ((ContainerNode *)current)->ChildrenCount()
		&& ((ContainerNode *)current)->ChildrenAt(index)->NodeType()==Symbol)
	{
		SymbolNode * temp=(SymbolNode *) ( ((ContainerNode *)current)->ChildrenAt(index) );
		MovieClipNode * newNode=new MovieClipNode();
		newNode->CopyProperty(temp);
		SymbolNode* temp1;
		int n=temp->ChildrenCount();
		for(int i=0;i!=n;++i)
		{
			temp1=new SymbolNode();
			temp1->AddChildren(temp->PopChildrenAt(0));
			newNode->AddChildren(temp1);
		}
		newNode->UpdateCache();
		((ContainerNode *)current)->ReplaceChildrenAt(index,newNode);
		SetDirty();
	}
	else
		throw "Invalid convert!!!";
}
void DataManipulator::AddRect(double width,double height,int index)
{
	if(current->NodeType()==Symbol)
	{
		if(index<0||index>=((ContainerNode *)current)->ChildrenCount())
			index=((ContainerNode *)current)->ChildrenCount();
		((ContainerNode *)current)->AddChildren(new RectNode(width,height),index);
		SetDirty();
	}
	else		
		throw "Failed to Create!!!";
}
void DataManipulator::AddEclipse(double width,double height,int index)
{
	if(current->NodeType()==Symbol)
	{
		if(index<0||index>=((ContainerNode *)current)->ChildrenCount())
			index=((ContainerNode *)current)->ChildrenCount();
		((ContainerNode *)current)->AddChildren(new EclipseNode(width,height),index);
		SetDirty();
	}
	else		
		throw "Failed to Create!!!";
}
void DataManipulator::AddPicture(QString path,int index)
{
	if(current->NodeType()==Symbol)
	{
		if(index<0||index>=((ContainerNode *)current)->ChildrenCount())
			index=((ContainerNode *)current)->ChildrenCount();
		((ContainerNode *)current)->AddChildren(new PictureNode(path),index);
		SetDirty();
	}
	else		
		throw "Failed to Create!!!";
}
void DataManipulator::AddPicture(QImage image,int index)
{
	if(current->NodeType()==Symbol)
	{
		if(index<0||index>=((ContainerNode *)current)->ChildrenCount())
			index=((ContainerNode *)current)->ChildrenCount();
		((ContainerNode *)current)->AddChildren(new PictureNode(image),index);
		SetDirty();
	}
	else		
		throw "Failed to Create!!!";
}
void DataManipulator::AddText(QString text,QFont font,int index)
{
	if(current->NodeType()==Symbol)
	{
		if(index<0||index>=((ContainerNode *)current)->ChildrenCount())
			index=((ContainerNode *)current)->ChildrenCount();
		((ContainerNode *)current)->AddChildren(new WordShapeNode(text,font),index);
		SetDirty();
	}
	else		
		throw "Failed to Create!!!";
}
void DataManipulator::BeginPath(int index)
{
	if(current->NodeType()==Symbol)
	{
		if(index<0||index>=((ContainerNode *)current)->ChildrenCount())
			index=((ContainerNode *)current)->ChildrenCount();
		((ContainerNode *)current)->AddChildren(new PathNode(),index);
		enterHistory.push(current);
		current=((ContainerNode *)current)->ChildrenAt(index);
		SetDirty();
	}
	else		
		throw "Failed to Create!!!";
}
void DataManipulator::EndPath()
{
	if(current->NodeType()==Path)
	{
		EscapeToParent();
	}
	else		
		throw "Invalid command!!!";
}
void DataManipulator::AddLinearPoint(const QPointF& pt)
{
	if(current->NodeType()==Path)
	{
		((PathNode *)current)->AddLinearPoint(pt);
		SetDirty();
	}
	else		
		throw "Invalid command!!!";
}
void DataManipulator::AddSharpPoint(const QPointF& pt,const QPointF& cpt)
{
	if(current->NodeType()==Path)
	{
		((PathNode *)current)->AddSharpPoint(pt,cpt);
		SetDirty();
	}
	else		
		throw "Invalid command!!!";
}
void DataManipulator::AddSmoothPoint(const QPointF& pt,const QPointF& cpt)
{
	if(current->NodeType()==Path)
	{
		((PathNode *)current)->AddSmoothPoint(pt,cpt);
		SetDirty();
	}
	else		
		throw "Invalid command!!!";
}
void DataManipulator::AddSymmetricalPoint(const QPointF& pt,const QPointF& cpt)
{
	if(current->NodeType()==Path)
	{
		((PathNode *)current)->AddSymmetricalPoint(pt,cpt);
		SetDirty();
	}
	else		
		throw "Invalid command!!!";
}
void DataManipulator::ChangePointType(int index,ePtType type)
{
	if(current->NodeType()==Path)
	{
		((PathNode *)current)->ChangePointType(index,type);
		SetDirty();
	}
	else		
		throw "Invalid command!!!";
}
const QPointF& DataManipulator::PointAt(int index)
{
	if(current->NodeType()==Path)
	{
		return ((PathNode *)current)->PointAt(index);
	}
	else		
		throw "Invalid command!!!";
}
const QPointF& DataManipulator::LControlPointAt(int index)
{
	if(current->NodeType()==Path)
	{
		return ((PathNode *)current)->LControlPointAt(index);
	}
	else		
		throw "Invalid command!!!";
}
const QPointF& DataManipulator::RControlPointAt(int index)
{
	if(current->NodeType()==Path)
	{
		return ((PathNode *)current)->RControlPointAt(index);
	}
	else		
		throw "Invalid command!!!";
}
void DataManipulator::MovePointAt(int index , double x,double y)
{
	if(current->NodeType()==Path)
	{
		((PathNode *)current)->MovePointAt(index,x,y);
		SetDirty();
	}
	else		
		throw "Invalid command!!!";
}
void DataManipulator::MoveLControlPointAt(int index , double x,double y)
{
	if(current->NodeType()==Path)
	{
		((PathNode *)current)->MoveLControlPointAt(index,x,y);
		SetDirty();
	}
	else		
		throw "Invalid command!!!";
}
void DataManipulator::MoveRControlPointAt(int index , double x,double y)
{
	if(current->NodeType()==Path)
	{
		((PathNode *)current)->MoveRControlPointAt(index,x,y);
		SetDirty();
	}
	else		
		throw "Invalid command!!!";
}
void DataManipulator::RemovePointAt(int index)
{
	if(current->NodeType()==Path)
	{
		((PathNode *)current)->RemovePointAt(index);
		SetDirty();
	}
	else		
		throw "Invalid command!!!";
}
int DataManipulator::PointIndexOf(double x,double y)
{
	if(current->NodeType()==Path)
	{
		return ((PathNode *)current)->PointIndexOf(x,y);
	}
	else		
		throw "Invalid command!!!";
}
void DataManipulator::ClosePath()
{
	if(current->NodeType()==Path)
	{
		((PathNode *)current)->ClosePath();
		SetDirty();
	}
	else
		throw "Invalid command!!!";
}
ePtType DataManipulator::PointType(int index)
{
	if(current->NodeType()==Path)
	{
		return ((PathNode *)current)->PointType(index);
	}
	else
		throw "Invalid command!!!";
}
void DataManipulator::RemoveNodeAt(int index)
{
	((ContainerNode *)current)->RemoveChildrenAt(index);
	SetDirty();
}
void DataManipulator::ExchangeNodeAt(int index1,int index2)
{
	((ContainerNode *)current)->Swap(index1,index2);
	SetDirty();
}

double DataManipulator::GetNodeProperty(propertyType pt,int time)
{
	if(current->IsAtomic())
	{
		AtomicNode * atom=(AtomicNode*) current;
		switch(pt)
		{
		case posx:
			return atom->GetPosX();
			break;
		case posy:
			return atom->GetPosY();
			break;
		case scalex:
			return atom->GetScaleX();
			break;
		case scaley:
			return atom->GetScaleY();
			break;
		case axisx:
			return atom->GetAxisX();
			break;
		case axisy:
			return atom->GetAxisY();
			break;
		case skewx:
			return atom->GetSkewX();
			break;
		case skewy:
			return atom->GetSkewY();
			break;
		case angle:
			return atom->GetAngle();
			break;
		case alpha:
			return atom->GetAlpha();
			break;
		}
	}
	else
	{
		ContainerNode * container=(ContainerNode*) current;
		switch(pt)
		{
		case posx:
			return container->GetPosX(time);
			break;
		case posy:
			return container->GetPosY(time);
			break;
		case scalex:
			return container->GetScaleX(time);
			break;
		case scaley:
			return container->GetScaleY(time);
			break;
		case axisx:
			return container->GetAxisX(time);
			break;
		case axisy:
			return container->GetAxisY(time);
			break;
		case skewx:
			return container->GetSkewX(time);
			break;
		case skewy:
			return container->GetSkewY(time);
			break;
		case angle:
			return container->GetAngle(time);
			break;
		case alpha:
			return container->GetAlpha(time);
			break;
		case visible:
			return container->GetVisible(time);
			break;
		}
	}
	throw "Invalid command!!!";
}
Ease::EaseType	DataManipulator::GetNodePropertyEase(propertyType pt,int time)
{
	if(current->IsAtomic())
	{
		return Ease::ALLStart;
	}
	else
	{
		ContainerNode * container=(ContainerNode*) current;
		switch(pt)
		{
		case posx:
			return container->GetPosXEase(time);
			break;
		case posy:
			return container->GetPosYEase(time);
			break;
		case scalex:
			return container->GetScaleXEase(time);
			break;
		case scaley:
			return container->GetScaleYEase(time);
			break;
		case axisx:
			return container->GetAxisXEase(time);
			break;
		case axisy:
			return container->GetAxisYEase(time);
			break;
		case skewx:
			return container->GetSkewXEase(time);
			break;
		case skewy:
			return container->GetSkewYEase(time);
			break;
		case angle:
			return container->GetAngleEase(time);
			break;
		case alpha:
			return container->GetAlphaEase(time);
			break;
		case visible:
			return container->GetVisibleEase(time);
			break;
		}
	}
	throw "Invalid command!!!";
}
void DataManipulator::SetNodeProperty(propertyType pt,double value,int time,Ease::EaseType ease)
{
	if(current->IsAtomic())
	{
		AtomicNode * atom=(AtomicNode*) current;
		switch(pt)
		{
		case posx:
			atom->SetPosX(value);
			break;
		case posy:
			atom->SetPosY(value);
			break;
		case scalex:
			atom->SetScaleX(value);
			break;
		case scaley:
			atom->SetScaleY(value);
			break;
		case axisx:
			atom->SetAxisX(value);
			break;
		case axisy:
			atom->SetAxisY(value);
			break;
		case skewx:
			atom->SetSkewX(value);
			break;
		case skewy:
			atom->SetSkewY(value);
			break;
		case angle:
			atom->SetAngle(value);
			break;
		case alpha:
			atom->SetAlpha(value);
			break;
		}
	}
	else
	{
		ContainerNode * container=(ContainerNode*) current;
		switch(pt)
		{
		case posx:
			container->SetPosX(value,time,ease);
			break;
		case posy:
			container->SetPosY(value,time,ease);
			break;
		case scalex:
			container->SetScaleX(value,time,ease);
			break;
		case scaley:
			container->SetScaleY(value,time,ease);
			break;
		case axisx:
			container->SetAxisX(value,time,ease);
			break;
		case axisy:
			container->SetAxisY(value,time,ease);
			break;
		case skewx:
			container->SetSkewX(value,time,ease);
			break;
		case skewy:
			container->SetSkewY(value,time,ease);
			break;
		case angle:
			container->SetAngle(value,time,ease);
			break;
		case alpha:
			container->SetAlpha(value,time,ease);
			break;
		case visible:
			container->SetVisible(value,time,ease);
			break;
		}
	}
	SetDirty();
}



void DataManipulator::RemoveKeyFrames(int flags,int time,int index)
{
	if(index==-1)
	{
		if(!(current->IsAtomic()))
			((ContainerNode*)current)->removeKeyFrames(flags,time);
	}
	else
	{
		if(!(current->IsAtomic())&&index>-1&&index< (((ContainerNode*)current)->ChildrenCount()) 
			&&!(((ContainerNode*)(((ContainerNode*)current)->ChildrenAt(index)))->IsAtomic())
			)
			((ContainerNode*)(((ContainerNode*)current)->ChildrenAt(index)))->removeKeyFrames(flags,time);
	}
}


QVector<int > DataManipulator::GetKeyFrames(int flags,int index)const
{
	if(index==-1)
	{
		if(!(current->IsAtomic()))
			return ((ContainerNode*)current)->getKeyFrames(flags);
	}
	else
	{
		if(!(current->IsAtomic())&&index>-1&&index< (((ContainerNode*)current)->ChildrenCount()) 
			&&!(((ContainerNode*)(((ContainerNode*)current)->ChildrenAt(index)))->IsAtomic())
			)
			return ((ContainerNode*)(((ContainerNode*)current)->ChildrenAt(index)))->getKeyFrames(flags);
	}
	return QVector<int>();
}

void DataManipulator::ToRoot()
{
	current=root;
	enterHistory.clear();

}
const DisplayNode* DataManipulator::CurrentPt()
{
	return current;
}
const DisplayNode* DataManipulator::ChildPtAt(int index)
{
	if(index<0||index>=((ContainerNode *)current)->ChildrenCount())		
		throw "Invalid index!!!";
	return ((ContainerNode *)current)->ChildrenAt(index);
}
const QImage& DataManipulator::CurrentImage()
{
	return current->GetImage();
}
const QImage& DataManipulator::ChildImageAt(int index)
{
	if(index<0||index>=((ContainerNode *)current)->ChildrenCount())		
		throw "Invalid index!!!";
	return ((ContainerNode *)current)->ChildrenAt(index)->GetImage();
}
QPen& DataManipulator::CurrentPen()
{
	SetDirty();
	if(current->IsAtomic()&&current->NodeType()!=Picture)
		return ((ShapeNode *)current)->LinePenRef();
	throw "not a shape";
}
QBrush& DataManipulator::CurrentBrush()
{
	SetDirty();
	if(current->IsAtomic()&&current->NodeType()!=Picture)
		return ((ShapeNode *)current)->FillBrushRef();
	throw "not a shape";
}
int DataManipulator::ChildrenCount()
{
	if(current->IsAtomic())
		throw "Invalid command!!!";
	else
		return ((ContainerNode *)current)->ChildrenCount();
}
eNodeType DataManipulator::CurrentNodeType()
{
	return current->NodeType();
}
eNodeType DataManipulator::ChildrenNodeType(int index)
{
	if(index<0||index>=((ContainerNode *)current)->ChildrenCount())
		throw "Invalid index!!!";
	return ((ContainerNode *)current)->ChildrenAt(index)->NodeType();
}
int DataManipulator::ChildUnderPointAt(QPointF pt)
{
	return ((ContainerNode *)current)->GetChildUnderPoint(pt);
}
QVector<int> DataManipulator::ChildrenUnderPointAt(QPointF pt)
{
	return ((ContainerNode *)current)->GetChildrenUnderPoint(pt);
}


int DataManipulator::IndexOfChild(DisplayNode * child)
{
	return ((ContainerNode *)current)->IndexOf(child);
}

int DataManipulator::IndexOfCurrent()
{
	if(enterHistory.size())
		return ((ContainerNode *)enterHistory.top())->IndexOf(current);
	else
		return -1;
}


void DataManipulator::AddNodeAt(DisplayNode * ref,int index  )
{
	if(index<0||index>=((ContainerNode *)current)->ChildrenCount())
		index=-1;
	((ContainerNode *)current)->AddChildren(ref,index);
	SetDirty();
}


void DataManipulator::Update()
{
	current->UpdateCache();
}

void DataManipulator::WordFont(const QFont& font)
{
	SetDirty();
	if(current->NodeType()==WordShape)
		((WordShapeNode *)current)->SetFont(font);
	else
		throw "not a word";
}
void DataManipulator::WordText(const QString& text)
{
	SetDirty();
	if(current->NodeType()==WordShape)
		((WordShapeNode *)current)->SetText(text);
	else
		throw "not a word";
}